#include "Minimap.h"
#include "CodeEdit.h"
#include "Highlighter.h"

#include <QTextBlock>
#include <QApplication>

Minimap::Minimap(CodeEdit * edit)
    : QPlainTextEdit(edit)
    , _edit(edit)
{
    Q_ASSERT(edit);

    setReadOnly(true);
    setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    setFocusPolicy(Qt::NoFocus);
    setFrameStyle(QFrame::NoFrame);
    setContentsMargins(0,0,0,0);
    setMouseTracking(true);

    viewport()->setCursor(Qt::ArrowCursor);
}

void Minimap::highlightViewportArea(const QString & selected_text, QTextDocument::FindFlags find_options)
{
    QPair<int,int>                   lines      = _edit->getViewportLines();
    QColor                           line_color = QColor(palette().color(backgroundRole())).lighter(100);
    QTextCursor                      cursor     = textCursor();
    QList<QTextEdit::ExtraSelection> extraSelections;

    for(int line = lines.first; line <= lines.second; line++) 
    {
        cursor.setPosition(document()->findBlockByNumber(line).position());

        if (line == lines.first)
            setTextCursor(cursor);

        QTextEdit::ExtraSelection selection;

        selection.format.setBackground(line_color);
        selection.format.setProperty(QTextFormat::FullWidthSelection, true);
        selection.cursor = cursor;
        selection.cursor.clearSelection();

        extraSelections.append(selection);
    }

    int selection_min_length = 1;

    if (selected_text.length() >= selection_min_length) {
        QTextCharFormat format;

        format.setBackground(palette().brush(QPalette::Normal, QPalette::Highlight));
        format.setForeground(palette().brush(QPalette::Normal, QPalette::Highlight));

        QTextDocument * doc = document();
        QTextCursor     cur = doc->find(selected_text, 0, find_options);
        int             qua = 0;
        int             lim = 1000;

        while(!cur.isNull() && qua < lim) {
            QTextEdit::ExtraSelection selection;

            selection.cursor = cur;
            selection.format = format;

            if (cur != cursor)
                extraSelections.append(selection);

            cur = doc->find(selected_text, cur, find_options);
            qua ++;
        }
    }

    setExtraSelections(extraSelections);
    setTextCursor(cursor);
}

void Minimap::resetHighlighter(Highlighter * highlighter)
{
    if (_highlighter)
        delete _highlighter;

    if (highlighter)
        _highlighter = new Highlighter( document(), 
                                        highlighter, 
                                        _edit->params().semantic_postorder
                                            ? Highlighter::SemanticFormatOrder::Post
                                            : Highlighter::SemanticFormatOrder::Early,
                                        Highlighter::ContrastMode::Hight);
    else
        _highlighter = nullptr;
}

void Minimap::rehighlight()
{
    if (_highlighter)
        _highlighter->rehighlight();
}

bool Minimap::event(QEvent* event)
{
    if (event->type() == QEvent::Leave) {
        _left_button_pressed = false;
        event->accept();
        return false;
    }

    if (event->type() == QEvent::PaletteChange)
        if (_highlighter) {
            _highlighter->setupCurrentFormat(palette());
            _highlighter->rehighlight();
        }

    return QPlainTextEdit::event(event);
}

void Minimap::wheelEvent(QWheelEvent * event)
{
    if (event->modifiers() != Qt::ControlModifier) {
        /// \todo Рабочий вариант, но лучше найти правильный способ увеличения чувствительности.
        int multiplier = 12 / (font().pointSize() > 0 ? font().pointSize() : 1);
        for(int i=0; i < multiplier; ++i)
            QPlainTextEdit::wheelEvent(event);
    }
}

void Minimap::contextMenuEvent(QContextMenuEvent * event)
{
    event->ignore();
}

void Minimap::mousePressEvent(QMouseEvent * event)
{
    if (event->button() & Qt::LeftButton) {
        QApplication::setOverrideCursor(Qt::PointingHandCursor);
        _left_button_pressed = true;
    }

    event->ignore();
}

void Minimap::mouseReleaseEvent(QMouseEvent * event)
{
    if (event->button() & Qt::LeftButton) {
        emitRequestPosition(event->pos());
        QApplication::restoreOverrideCursor();
        _left_button_pressed = false;
    }

    event->ignore();
}

void Minimap::mouseMoveEvent(QMouseEvent * event)
{
    if (_left_button_pressed) 
        emitRequestPosition(event->pos());

    event->ignore();
}

void Minimap::mouseDoubleClickEvent(QMouseEvent * event)
{
    event->ignore();
}

void Minimap::emitRequestPosition(QPoint point)
{
    QTextCursor cursor = cursorForPosition(point);
    int         pos    = cursor.position();

    if (_last_pos != pos) {
        _last_pos = pos;
        emit requestPosition(pos);
    }
}

